package com.wit.example;


import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.Switch;
import android.widget.Toast;


import com.tli.bluetooth.PreferenceData;
import com.tli.common.AAData;
import com.tli.common.ATData;
import com.tli.common.CommonAttribute;
import com.tli.common.FsrData;
import com.tli.common.GAData;
import com.tli.common.MVData;
import com.tli.common.MpuData;
import com.tli.common.RunningData;
import com.tli.common.RunningSectionData;
import com.tli.common.SAData;
import com.tli.utils.ApiBuildConfig;
import com.tli.walkanalysis.DeviceInfo;
import com.tli.walkanalysis.ScanData;
import com.tli.walkanalysis.UIServiceCallbackInterface;
import com.tli.walkanalysis.WalkingAnalysisServiceApi;
import com.wit.example.util.CSVWriter;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;


import java.io.File;
import java.sql.Date;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
import java.util.concurrent.LinkedBlockingQueue;


public class MainActivity3 extends AppCompatActivity implements UIServiceCallbackInterface {
    private final static String TAG = MainActivity.class.getSimpleName();
    private String[] permissions = {
            android.Manifest.permission.READ_EXTERNAL_STORAGE,
            android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
            android.Manifest.permission.ACCESS_COARSE_LOCATION,
            android.Manifest.permission.BLUETOOTH_CONNECT,
            android.Manifest.permission.ACCESS_FINE_LOCATION
    };
    private static final int MULTIPLE_PERMISSIONS = 101;


    private Context mContext;
    private static final int REQUEST_ENABLE_BT = 1;
    private BluetoothAdapter mBluetoothAdapter;
    private String mLeftAddress = "00:00:00:00:00:00";
    private boolean mLeftScaned = false;

    private String mRightAddress = "00:00:00:00:00:01";
    private boolean mRightScaned = false;

    private String[] mDeviceAddresses = new String[2];

    boolean mFootFileViewEnable = false;
    private Object mWaitLock = new Object();
    private static LinkedBlockingQueue<DataQueuePacket> dataQueue = null;

    private File internalStorageDir;

    class DataQueuePacket {
        int lr_type;
        byte[] org;

        public void setData(int lrtype, byte[] bytedata) {
            this.lr_type = lrtype;
            this.org = bytedata.clone();
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main3);
        mContext = this;
        checkPermissions();
        internalStorageDir = getFilesDir();

        boolean emulator = false;
        if (!emulator) {
            if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
                Toast.makeText(this, "Ble not supported", Toast.LENGTH_SHORT).show();
                finish();
            }

            final BluetoothManager mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
            mBluetoothAdapter = mBluetoothManager.getAdapter();
            if (mBluetoothAdapter == null) {
                Toast.makeText(this, "Ble not supported", Toast.LENGTH_SHORT).show();
                finish();
                return;
            }

            if (!mBluetoothAdapter.isEnabled()) {
                Intent enableBtIntent = new Intent(
                        BluetoothAdapter.ACTION_REQUEST_ENABLE);
                if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
                    // TODO: Consider calling
                    //    ActivityCompat#requestPermissions
                    // here to request the missing permissions, and then overriding
                    //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
                    //                                          int[] grantResults)
                    // to handle the case where the user grants the permission. See the documentation
                    // for ActivityCompat#requestPermissions for more details.
                    return;
                }
                startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
                return;
            }
        }


        dataQueue = new LinkedBlockingQueue<DataQueuePacket>();
        dataQueue.clear();

        mFootFileViewEnable = true;
        Thread th = new Thread(mFootFileViewRunnable);
        th.start();

        int ret = WalkingAnalysisServiceApi.createBleService(this,this);
        Log.e(TAG,"create ret " + ret);
    }

    @Override
    protected void onDestroy() {
        WalkingAnalysisServiceApi.destroyBleService();
        super.onDestroy();
    }

    int new_type_index = 0;

    public void OnButtonClick(View v){
        if(v == findViewById(R.id.scanstop)){
            WalkingAnalysisServiceApi.stopDeviceScan();
        }else if(v == findViewById(R.id.scanstart)){
            WalkingAnalysisServiceApi.startDeviceScan();
        }else if(v== findViewById(R.id.realstart)){
            int ret =WalkingAnalysisServiceApi.StartAnalysis();
            Log.e(TAG,"StartAnalysis ret " + ret);
        }else if(v== findViewById(R.id.realstop)){
            int ret =WalkingAnalysisServiceApi.StopAnalysis();
        }else if(v== findViewById(R.id.connect)){
            String[] data = WalkingAnalysisServiceApi.getUserInfo();
            if (data != null) {
                // example
                data[WalkingAnalysisServiceApi.KEY_MY_FOOT_SIZE_INDEX] = "260";
                data[WalkingAnalysisServiceApi.KEY_MY_AGE_INDEX] = "30";
                data[WalkingAnalysisServiceApi.KEY_MY_HEIGHT_INDEX] = "180";
                data[WalkingAnalysisServiceApi.KEY_MY_WEIGHT_INDEX] = "70";
                data[WalkingAnalysisServiceApi.KEY_MY_SEX_INDEX] = "M";
                data[WalkingAnalysisServiceApi.KEY_MY_NAME_INDEX] = "user";
                data[WalkingAnalysisServiceApi.KEY_MY_EMAIL_INDEX] = "user@mgmail.co.kr";
                WalkingAnalysisServiceApi.setUserInfo(data);
            }
            data = WalkingAnalysisServiceApi.getDeviceAddress();
            if (data != null) {
             /*
            맨 처음 초기 값 , 기존에 저장해 둔 인솔 주소 , 새롭게 연결할 인솔 주소
            상황에 맞게 인솔 주소값을 등록하면 된다.
            First initial value, previously saved Insole address, newly connected Insole address
            You can register the Insol address value according to the situation.
            */
                mDeviceAddresses[0] = data[WalkingAnalysisServiceApi.KEY_LEFT_ADDRESS_INDEX];
                mDeviceAddresses[1] = data[WalkingAnalysisServiceApi.KEY_RIGHT_ADDRESS_INDEX];
                Log.e(TAG, "left insole addr " + mDeviceAddresses[0] + " right insole addr " + mDeviceAddresses[1]);
                if (mDeviceAddresses[0].equals("00:00:00:00:00:00") && mDeviceAddresses[1].equals("00:00:00:00:00:01")) {
                     /*
                     맨 처음 초기 값이며, 연결할 인솔을 검색한 후, 저장한 후에 연결 한다.
                     This is the initial value. Search for the insole to be connected, save it, and then connect.
                     */
                    data[WalkingAnalysisServiceApi.KEY_LEFT_ADDRESS_INDEX] = mLeftAddress;
                    data[WalkingAnalysisServiceApi.KEY_RIGHT_ADDRESS_INDEX] = mRightAddress;
                    WalkingAnalysisServiceApi.setDeviceAddress(data);
                }
                WalkingAnalysisServiceApi.setDeviceAddress(data);
            }
           /*

           WalkingAnalysisServiceApi.setDeviceAddress(data) 에 등록된 인솔로 연결한다.

           Connect to the insole registered in WalkingAnalysisServiceApi.setDeviceAddress(data).

           */
            WalkingAnalysisServiceApi.bleConnect((byte)0,false,false);
            //WalkingAnalysisServiceApi.bleConnect((byte)0,false,false);
        }else if(v== findViewById(R.id.disconnect)){
            WalkingAnalysisServiceApi.bleDisconnect();
        }else if(v== findViewById(R.id.setfinddevice)){
            /*
             연결된 인솔이 맞는지 확인 하기 위해 사용한다.
             명령을 내린 후 인솔을 흔든다. 그리고 아래 getDeviceFind(CommonAttribute.LEFT_TYPE) 함수를 클릭하여
             detect 되었는지 확인한다.

             Used to check whether the connected insole is correct.
             After giving the command, wave the lead. Then click the getDeviceFind(CommonAttribute.LEFT_TYPE) function below.
             Check whether it has been detected.

             用于检查连接的鞋垫是否正确。
             在给出命令后，挥动（可能是指靠近或远离设备以触发某种检测）引导物。然后，点击下面的getDeviceFind(CommonAttribute.LEFT_TYPE)函数。
             检查是否已经检测到（正确的鞋垫）。
            */
            boolean ret =WalkingAnalysisServiceApi.setDeviceFind(CommonAttribute.LEFT_TYPE);
            if (ret) {
                Toast.makeText(this, "setDeviceFind Successful", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(this, "setDeviceFind Failed", Toast.LENGTH_SHORT).show();
            }
        }else if(v== findViewById(R.id.getfinddevice)){
            // 연결된 인솔이 맞는지 확인 하기 위해 사용한다.
            // Used to check whether the connected insole is correct.
            // 用于检查连接的鞋垫是否正确
            boolean ret = WalkingAnalysisServiceApi.getDeviceFind(CommonAttribute.LEFT_TYPE);
            if (ret) {
                Toast.makeText(this, "connected insole Successful", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(this, "connected insole Failed", Toast.LENGTH_SHORT).show();
            }
        }else if(v== findViewById(R.id.setuserinfo)){
            /*
            연결된 상태일 때 사용한다.
            처음 시작시 사용자 정보를 인솔에 넣어준다.
            Use when connected.
            When first starting, user information is entered into the insole.
            在连接后使用。
            首次启动时，用户信息将被输入到鞋垫中。
            */

            boolean ret =WalkingAnalysisServiceApi.setDeviceInformation((byte)CommonAttribute.REGISTER_TYPE_NEW_REGISTER);
        }else if(v== findViewById(R.id.setuserinfochange)){
            /*
            연결된 상태일 때 사용한다.
            사용자 정보가 변경되었을 때 넣어준다.
            Use when connected.连接时使用。
            Entered when user information is changed.当用户信息发生变化时输入（或记录）。
            */
            boolean ret =WalkingAnalysisServiceApi.setDeviceInformation((byte)CommonAttribute.REGISTER_TYPE_CHANGE_USERINFO);
        }else if(v== findViewById(R.id.workinfo)){
            /*
            연결된 상태일 때 사용한다.
            현재 걸음 정보를 가져온다.
            ATData(OnATData callback), AAData(OnAAData callback), GAData(OnGAData callback), MVData(OnMVData callback)
            Use when connected.连接时使用。
            Get current step information.获取当前步数信息。
            ATData(OnATData callback), AAData(OnAAData callback), GAData(OnGAData callback), MVData(OnMVData callback)
            */
            int ret =WalkingAnalysisServiceApi.setRequestStatus((byte) (CommonAttribute.REQUEST_STATUS_AT_ENABLE | CommonAttribute.REQUEST_STATUS_AA_ENABLE | CommonAttribute.REQUEST_STATUS_GA_ENABLE | CommonAttribute.REQUEST_STATUS_MV_ENABLE));
        }
        else if(v== findViewById(R.id.getfwinfo)){
            /*
            연결된 상태일 때 사용한다.
            현재 인솔의 fw 정보를 가져온다.
            Use when connected.
            Retrieves fw information of the current insole.
            // 当连接时使用。
            // 检索当前鞋垫的固件信息。
            */
            boolean ret =WalkingAnalysisServiceApi.getFirmwareStatus();
        }
        else if(v== findViewById(R.id.clearall)){
            /*
            연결된 상태일 때 사용한다.
            인솔 정보를 모두 삭제한다.
            Use when connected.
            Delete all information.
            当连接时使用。
            删除所有信息。
            */
            boolean ret =WalkingAnalysisServiceApi.setClearAll();
        }
        else if(v== findViewById(R.id.historyclear)){
            /*
            연결된 상태일 때 사용한다.
            히스토리 정보(Day and Hour)를 삭제한다
            Use when connected.
            Delete history information (Day and Hour)
            当连接时使用。
            删除历史信息。
            */
            boolean ret =WalkingAnalysisServiceApi.setClearHistory(CommonAttribute.STORAGE_DAY_HOUR_CLEAR);

            /**
             * History Data flag reset
             * 플래그를 reset 하여. 히스토리를 다시 받을 수 있도록 한다.
             * By resetting the flag. Make sure you can get the history again.
             * 通过重置标志，确保你可以再次获取历史记录。
             */
            //ret =WalkingAnalysisServiceApi.setClearHistory(CommonAttribute.STORAGE_REPORTED_FLAG_CLEAR);
        }
        else if(v == findViewById(R.id.at_day)){
            Switch s = findViewById(R.id.history_type_switch);
            if(s.isChecked()){
                Log.e(TAG,"Check");
                new_type_index++;
                // 인텍스 변화만 준다.(It only gives index changes.)
                if(new_type_index>250)
                    new_type_index = 0;

                //왼발, 오른발 각자 데이터를 받는다(Left and right feet each receive data)
                //(get left day history data)
                WalkingAnalysisServiceApi.RequestStorageATDataLRType(CommonAttribute.LEFT_TYPE,CommonAttribute.STORAGE_TYPE_DAY ,new_type_index, CommonAttribute.STORAGE_REPORT_TYPE_NEW);
                //(get right day history data)
                //WalkingAnalysisServiceApi.RequestStorageATDataLRType(CommonAttribute.RIGHT_TYPE,CommonAttribute.STORAGE_TYPE_DAY ,new_type_index, CommonAttribute.STORAGE_REPORT_TYPE_NEW);

            }else{
                Log.e(TAG,"Un Check");
                /*
                GMT-0으로 적용되어 나오기 때문에 이 사용법은 디버깅할 때 주로 사용.
                실제로 아래와 같이 입력하면,
                gmt +09:00(korea) 이라서 만일 24일로 입력이 되면 20211124-085959 로 나옴.

                Since it is applied as GMT-0, this method is mainly used when debugging.
                In fact, if you enter the following,
                Since gmt +09:00(korea), if the 24th is entered, it comes out as 20211124-085959.
                */

                EditText e = findViewById(R.id.editTextNumberDecimalDay);
                if(e.getText().toString().length() <= 0)
                    return;

                int day_index = Integer.parseInt(e.getText().toString());

                //(get left day history data)
                WalkingAnalysisServiceApi.RequestStorageATDataLRType(CommonAttribute.LEFT_TYPE,CommonAttribute.STORAGE_TYPE_DAY ,day_index, CommonAttribute.STORAGE_REPORT_TYPE_SELECT);
                //or
                //(get right day history data)
                //WalkingAnalysisServiceApi.RequestStorageATDataLRType(CommonAttribute.RIGHT_TYPE,CommonAttribute.STORAGE_TYPE_DAY ,day_index, CommonAttribute.STORAGE_REPORT_TYPE_SELECT);

            }
        }
        else if(v == findViewById(R.id.at_hour)){
            Switch s = findViewById(R.id.history_type_switch);
            if(s.isChecked()){
                Log.e(TAG,"Check");
                new_type_index++;
                // 인텍스 변화만 준다.(It only gives index changes.)
                if(new_type_index>250)
                    new_type_index = 0;

                //왼발, 오른발 각자 데이터를 받는다(Left and right feet each receive data)
                //(get left hour history data)
                WalkingAnalysisServiceApi.RequestStorageATDataLRType(CommonAttribute.LEFT_TYPE,CommonAttribute.STORAGE_TYPE_HOUR ,new_type_index, CommonAttribute.STORAGE_REPORT_TYPE_NEW);
                //(get right hour history data)
                //WalkingAnalysisServiceApi.RequestStorageATDataLRType(CommonAttribute.RIGHT_TYPE,CommonAttribute.STORAGE_TYPE_HOUR ,new_type_index, CommonAttribute.STORAGE_REPORT_TYPE_NEW);
            }else{
                Log.e(TAG,"Un Check");
                /*
                GMT-0으로 적용되어 나오기 때문에 이 사용법은 디버깅할 때 주로 사용.
                실제로 아래와 같이 입력하면,
                gmt +09:00 이라서 만일 23시로 입력이 되면  20211125-075959 로 나옴.
                gmt +09:00 이라서 만일 24시로 입력이 되면 20211125-085959 로 나옴.

                Since it is applied as GMT-0, this method is mainly used when debugging.
                In fact, if you enter the following,
                GMT +09:00(korea), so if 23:00 is entered, it comes out as 20211125-075959.
                GMT +09:00(korea), so if 24 hours is entered, it comes out as 20211125-085959.
                */

                EditText e = findViewById(R.id.editTextNumberDecimalHour);
                if(e.getText().toString().length() <= 0)
                    return;
                int day_index = Integer.parseInt(e.getText().toString());

                //왼발, 오른발 각자 데이터를 받는다(Left and right feet each receive data)
                //(get left hour history data)
                WalkingAnalysisServiceApi.RequestStorageATDataLRType(CommonAttribute.LEFT_TYPE,CommonAttribute.STORAGE_TYPE_HOUR ,day_index, CommonAttribute.STORAGE_REPORT_TYPE_SELECT);
                //or
                //(get right hour history data)
                //WalkingAnalysisServiceApi.RequestStorageATDataLRType(CommonAttribute.RIGHT_TYPE,CommonAttribute.STORAGE_TYPE_HOUR ,day_index, CommonAttribute.STORAGE_REPORT_TYPE_SELECT);

            }
        }
        else if(v == findViewById(R.id.aa_day)){
            Switch s = findViewById(R.id.history_type_switch);
            if(s.isChecked()){
                Log.e(TAG,"Check");
                new_type_index++;
                // 인텍스 변화만 준다.(It only gives index changes.)
                if(new_type_index>250)
                    new_type_index = 0;

                //왼발, 오른발 각자 데이터를 받는다(Left and right feet each receive data)
                //(get left day history data)
                WalkingAnalysisServiceApi.RequestStorageAADataLRType(CommonAttribute.LEFT_TYPE,CommonAttribute.STORAGE_TYPE_DAY ,new_type_index, CommonAttribute.STORAGE_REPORT_TYPE_NEW);
                //(get right day history data)
                //WalkingAnalysisServiceApi.RequestStorageAADataLRType(CommonAttribute.RIGHT_TYPE,CommonAttribute.STORAGE_TYPE_DAY ,new_type_index, CommonAttribute.STORAGE_REPORT_TYPE_NEW);

            }else{
                Log.e(TAG,"Un Check");
                /*
                GMT-0으로 적용되어 나오기 때문에 이 사용법은 디버깅할 때 주로 사용.
                실제로 아래와 같이 입력하면,
                gmt +09:00(korea) 이라서 만일 24일로 입력이 되면 20211124-085959 로 나옴.

                Since it is applied as GMT-0, this method is mainly used when debugging.
                In fact, if you enter the following,
                Since gmt +09:00(korea), if the 24th is entered, it comes out as 20211124-085959.
                */


                EditText e = findViewById(R.id.editTextNumberDecimalDay);
                if(e.getText().toString().length() <= 0)
                    return;
                int day_index = Integer.parseInt(e.getText().toString());

                //왼발, 오른발 각자 데이터를 받는다(Left and right feet each receive data)
                //(get left day history data)
                WalkingAnalysisServiceApi.RequestStorageAADataLRType(CommonAttribute.LEFT_TYPE,CommonAttribute.STORAGE_TYPE_DAY ,day_index, CommonAttribute.STORAGE_REPORT_TYPE_SELECT);
                //or
                //(get right day history data)
                //WalkingAnalysisServiceApi.RequestStorageAADataLRType(CommonAttribute.RIGHT_TYPE,CommonAttribute.STORAGE_TYPE_DAY ,day_index, CommonAttribute.STORAGE_REPORT_TYPE_SELECT);

            }
        }
        else if(v == findViewById(R.id.aa_hour)){
            Switch s = findViewById(R.id.history_type_switch);
            if(s.isChecked()){
                Log.e(TAG,"Check");
                new_type_index++;
                // 인텍스 변화만 준다.(It only gives index changes.)
                if(new_type_index>250)
                    new_type_index = 0;

                //왼발, 오른발 각자 데이터를 받는다(Left and right feet each receive data)
                //(get left hour history data)
                WalkingAnalysisServiceApi.RequestStorageAADataLRType(CommonAttribute.LEFT_TYPE,CommonAttribute.STORAGE_TYPE_HOUR ,new_type_index, CommonAttribute.STORAGE_REPORT_TYPE_NEW);
                //(get right hour history data)
                //WalkingAnalysisServiceApi.RequestStorageAADataLRType(CommonAttribute.RIGHT_TYPE,CommonAttribute.STORAGE_TYPE_HOUR ,new_type_index, CommonAttribute.STORAGE_REPORT_TYPE_NEW);
            }else{
                Log.e(TAG,"Un Check");
                /*
                GMT-0으로 적용되어 나오기 때문에 이 사용법은 디버깅할 때 주로 사용.
                실제로 아래와 같이 입력하면,
                gmt +09:00 이라서 만일 23시로 입력이 되면  20211125-075959 로 나옴.
                gmt +09:00 이라서 만일 24시로 입력이 되면 20211125-085959 로 나옴.

                Since it is applied as GMT-0, this method is mainly used when debugging.
                In fact, if you enter the following,
                GMT +09:00(korea), so if 23:00 is entered, it comes out as 20211125-075959.
                GMT +09:00(korea), so if 24 hours is entered, it comes out as 20211125-085959.
                */

                EditText e = findViewById(R.id.editTextNumberDecimalHour);
                if(e.getText().toString().length() <= 0)
                    return;
                int day_index = Integer.parseInt(e.getText().toString());

                //왼발, 오른발 각자 데이터를 받는다(Left and right feet each receive data)
                //(get left hour history data)
                WalkingAnalysisServiceApi.RequestStorageAADataLRType(CommonAttribute.LEFT_TYPE,CommonAttribute.STORAGE_TYPE_HOUR ,day_index, CommonAttribute.STORAGE_REPORT_TYPE_SELECT);
                //or
                //(get right hour history data)
                //WalkingAnalysisServiceApi.RequestStorageAADataLRType(CommonAttribute.RIGHT_TYPE,CommonAttribute.STORAGE_TYPE_HOUR ,day_index, CommonAttribute.STORAGE_REPORT_TYPE_SELECT);

            }
        }
        else if(v == findViewById(R.id.ga_day)){
            Switch s = findViewById(R.id.history_type_switch);
            if(s.isChecked()){
                Log.e(TAG,"Check");
                new_type_index++;
                // 인텍스 변화만 준다.(It only gives index changes.)
                if(new_type_index>250)
                    new_type_index = 0;

                //왼발, 오른발 각자 데이터를 받는다(Left and right feet each receive data)
                //(get left day history data)
                WalkingAnalysisServiceApi.RequestStorageGAData(CommonAttribute.LEFT_TYPE,CommonAttribute.STORAGE_TYPE_DAY ,new_type_index, CommonAttribute.STORAGE_REPORT_TYPE_NEW);
                //(get right day history data)
                //WalkingAnalysisServiceApi.RequestStorageGAData(CommonAttribute.RIGHT_TYPE,CommonAttribute.STORAGE_TYPE_DAY ,new_type_index, CommonAttribute.STORAGE_REPORT_TYPE_NEW);

            }else{
                Log.e(TAG,"Un Check");
                /*
                GMT-0으로 적용되어 나오기 때문에 이 사용법은 디버깅할 때 주로 사용.
                실제로 아래와 같이 입력하면,
                gmt +09:00(korea) 이라서 만일 24일로 입력이 되면 20211124-085959 로 나옴.

                Since it is applied as GMT-0, this method is mainly used when debugging.
                In fact, if you enter the following,
                Since gmt +09:00(korea), if the 24th is entered, it comes out as 20211124-085959.
                */


                EditText e = findViewById(R.id.editTextNumberDecimalDay);
                if(e.getText().toString().length() <= 0)
                    return;
                int day_index = Integer.parseInt(e.getText().toString());

                //왼발, 오른발 각자 데이터를 받는다(Left and right feet each receive data)
                //(get left day history data)
                WalkingAnalysisServiceApi.RequestStorageGAData(CommonAttribute.LEFT_TYPE,CommonAttribute.STORAGE_TYPE_DAY ,day_index, CommonAttribute.STORAGE_REPORT_TYPE_SELECT);
                //or
                //(get right day history data)
                //WalkingAnalysisServiceApi.RequestStorageGAData(CommonAttribute.RIGHT_TYPE,CommonAttribute.STORAGE_TYPE_DAY ,day_index, CommonAttribute.STORAGE_REPORT_TYPE_SELECT);

            }
        }
        else if(v == findViewById(R.id.ga_hour)){
            Switch s = findViewById(R.id.history_type_switch);
            if(s.isChecked()){
                Log.e(TAG,"Check");
                new_type_index++;
                // 인텍스 변화만 준다.(It only gives index changes.)
                if(new_type_index>250)
                    new_type_index = 0;

                //왼발, 오른발 각자 데이터를 받는다(Left and right feet each receive data)
                //(get left hour history data)
                WalkingAnalysisServiceApi.RequestStorageGAData(CommonAttribute.LEFT_TYPE,CommonAttribute.STORAGE_TYPE_HOUR ,new_type_index, CommonAttribute.STORAGE_REPORT_TYPE_NEW);
                //(get right hour history data)
                //WalkingAnalysisServiceApi.RequestStorageGAData(CommonAttribute.RIGHT_TYPE,CommonAttribute.STORAGE_TYPE_HOUR ,new_type_index, CommonAttribute.STORAGE_REPORT_TYPE_NEW);
            }else{
                Log.e(TAG,"Un Check");
                /*
                GMT-0으로 적용되어 나오기 때문에 이 사용법은 디버깅할 때 주로 사용.
                실제로 아래와 같이 입력하면,
                gmt +09:00 이라서 만일 23시로 입력이 되면  20211125-075959 로 나옴.
                gmt +09:00 이라서 만일 24시로 입력이 되면 20211125-085959 로 나옴.

                Since it is applied as GMT-0, this method is mainly used when debugging.
                In fact, if you enter the following,
                GMT +09:00(korea), so if 23:00 is entered, it comes out as 20211125-075959.
                GMT +09:00(korea), so if 24 hours is entered, it comes out as 20211125-085959.
                */

                EditText e = findViewById(R.id.editTextNumberDecimalHour);
                if(e.getText().toString().length() <= 0)
                    return;
                int day_index = Integer.parseInt(e.getText().toString());

                //왼발, 오른발 각자 데이터를 받는다(Left and right feet each receive data)
                //(get left hour history data)
                WalkingAnalysisServiceApi.RequestStorageGAData(CommonAttribute.LEFT_TYPE,CommonAttribute.STORAGE_TYPE_HOUR ,day_index, CommonAttribute.STORAGE_REPORT_TYPE_SELECT);
                //or
                //(get right hour history data)
                //WalkingAnalysisServiceApi.RequestStorageGAData(CommonAttribute.RIGHT_TYPE,CommonAttribute.STORAGE_TYPE_HOUR ,day_index, CommonAttribute.STORAGE_REPORT_TYPE_SELECT);

            }
        }
        else if(v == findViewById(R.id.mv_day)){
            Switch s = findViewById(R.id.history_type_switch);
            if(s.isChecked()){
                Log.e(TAG,"Check");
                new_type_index++;
                // 인텍스 변화만 준다.(It only gives index changes.)
                if(new_type_index>250)
                    new_type_index = 0;

                //왼발, 오른발 각자 데이터를 받는다(Left and right feet each receive data)
                //(get left day history data)
                WalkingAnalysisServiceApi.RequestStorageMVData(CommonAttribute.LEFT_TYPE,CommonAttribute.STORAGE_TYPE_DAY ,new_type_index, CommonAttribute.STORAGE_REPORT_TYPE_NEW);
                //(get right day history data)
                //WalkingAnalysisServiceApi.RequestStorageMVData(CommonAttribute.RIGHT_TYPE,CommonAttribute.STORAGE_TYPE_DAY ,new_type_index, CommonAttribute.STORAGE_REPORT_TYPE_NEW);

            }else{
                Log.e(TAG,"Un Check");
                /*
                GMT-0으로 적용되어 나오기 때문에 이 사용법은 디버깅할 때 주로 사용.
                실제로 아래와 같이 입력하면,
                gmt +09:00(korea) 이라서 만일 24일로 입력이 되면 20211124-085959 로 나옴.

                Since it is applied as GMT-0, this method is mainly used when debugging.
                In fact, if you enter the following,
                Since gmt +09:00(korea), if the 24th is entered, it comes out as 20211124-085959.
                */


                EditText e = findViewById(R.id.editTextNumberDecimalDay);
                if(e.getText().toString().length() <= 0)
                    return;
                int day_index = Integer.parseInt(e.getText().toString());

                //왼발, 오른발 각자 데이터를 받는다(Left and right feet each receive data)
                //(get left day history data)
                WalkingAnalysisServiceApi.RequestStorageMVData(CommonAttribute.LEFT_TYPE,CommonAttribute.STORAGE_TYPE_DAY ,day_index, CommonAttribute.STORAGE_REPORT_TYPE_SELECT);
                //or
                //(get right day history data)
                //WalkingAnalysisServiceApi.RequestStorageMVData(CommonAttribute.RIGHT_TYPE,CommonAttribute.STORAGE_TYPE_DAY ,day_index, CommonAttribute.STORAGE_REPORT_TYPE_SELECT);

            }
        }
        else if(v == findViewById(R.id.mv_hour)){
            Switch s = findViewById(R.id.history_type_switch);
            if(s.isChecked()){
                Log.e(TAG,"Check");
                new_type_index++;
                // 인텍스 변화만 준다.(It only gives index changes.)
                if(new_type_index>250)
                    new_type_index = 0;

                //왼발, 오른발 각자 데이터를 받는다(Left and right feet each receive data)
                //(get left hour history data)
                WalkingAnalysisServiceApi.RequestStorageMVData(CommonAttribute.LEFT_TYPE,CommonAttribute.STORAGE_TYPE_HOUR ,new_type_index, CommonAttribute.STORAGE_REPORT_TYPE_NEW);
                //(get right hour history data)
                //WalkingAnalysisServiceApi.RequestStorageMVData(CommonAttribute.RIGHT_TYPE,CommonAttribute.STORAGE_TYPE_HOUR ,new_type_index, CommonAttribute.STORAGE_REPORT_TYPE_NEW);
            }else{
                Log.e(TAG,"Un Check");
                /*
                GMT-0으로 적용되어 나오기 때문에 이 사용법은 디버깅할 때 주로 사용.
                실제로 아래와 같이 입력하면,
                gmt +09:00 이라서 만일 23시로 입력이 되면  20211125-075959 로 나옴.
                gmt +09:00 이라서 만일 24시로 입력이 되면 20211125-085959 로 나옴.

                Since it is applied as GMT-0, this method is mainly used when debugging.
                In fact, if you enter the following,
                GMT +09:00(korea), so if 23:00 is entered, it comes out as 20211125-075959.
                GMT +09:00(korea), so if 24 hours is entered, it comes out as 20211125-085959.
                */

                EditText e = findViewById(R.id.editTextNumberDecimalHour);
                if(e.getText().toString().length() <= 0)
                    return;
                int day_index = Integer.parseInt(e.getText().toString());

                //왼발, 오른발 각자 데이터를 받는다(Left and right feet each receive data)
                //(get left hour history data)
                WalkingAnalysisServiceApi.RequestStorageMVData(CommonAttribute.LEFT_TYPE,CommonAttribute.STORAGE_TYPE_HOUR ,day_index, CommonAttribute.STORAGE_REPORT_TYPE_SELECT);
                //or
                //(get right hour history data)
                //WalkingAnalysisServiceApi.RequestStorageMVData(CommonAttribute.RIGHT_TYPE,CommonAttribute.STORAGE_TYPE_HOUR ,day_index, CommonAttribute.STORAGE_REPORT_TYPE_SELECT);

            }
        }
        else if(v == findViewById(R.id.fwupdate)){
            int fwVer = 83;
            String fwFileNmL = "IKW_V83_20240105_L.ZIP";
            //WalkingAnalysisServiceApi.FirmwareUpdateStart2("EB:AB:62:72:B5:7A", "PSPODL", CommonAttribute.LEFT_TYPE, fwFileNmL, true, (byte) 8, true);
        }


    }


    public void CreateWalkAnalysisService(){
        int ret = WalkingAnalysisServiceApi.createBleService(this,this);
        Log.e(TAG, "createBleService " + ret);
    }

    private boolean checkPermissions() {
        int result;
        List<String> permissionList = new ArrayList<>();
        for (String pm : permissions) {
            result = ContextCompat.checkSelfPermission(this, pm);
            if (result != PackageManager.PERMISSION_GRANTED) { //사용자가 해당 권한을 가지고 있지 않을 경우 리스트에 해당 권한명 추가
                permissionList.add(pm);
            }
        }
        if (!permissionList.isEmpty()) { //권한이 추가되었으면 해당 리스트가 empty가 아니므로 request 즉 권한을 요청합니다.
            ActivityCompat.requestPermissions(this, permissionList.toArray(new String[permissionList.size()]), MULTIPLE_PERMISSIONS);
            return false;
        }
        return true;
    }

    //아래는 권한 요청 Callback 함수입니다. PERMISSION_GRANTED로 권한을 획득했는지 확인할 수 있습니다. 아래에서는 !=를 사용했기에
    //권한 사용에 동의를 안했을 경우를 if문으로 코딩되었습니다.
    @Override
    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode) {
            case MULTIPLE_PERMISSIONS: {
                if (grantResults.length > 0) {
                    for (int i = 0; i < permissions.length; i++) {
                        if (permissions[i].equals(this.permissions[0])) {
                            if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
                                showNoPermissionToastAndFinish();
                            }
                        } else if (permissions[i].equals(this.permissions[1])) {
                            if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
                                showNoPermissionToastAndFinish();

                            }
                        } else if (permissions[i].equals(this.permissions[2])) {
                            if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
                                showNoPermissionToastAndFinish();
                            }
                        }
                    }
                } else {
                    showNoPermissionToastAndFinish();
                }
                return;
            }
        }
    }

    //권한 획득에 동의를 하지 않았을 경우 아래 Toast 메세지를 띄우며 해당 Activity를 종료시킵니다.
    private void showNoPermissionToastAndFinish() {
        Toast.makeText(this, "권한 요청에 동의 해주셔야 이용 가능합니다. 설정에서 권한 허용 하시기 바랍니다.", Toast.LENGTH_SHORT).show();
        //finish();
    }

    void ServiceStartCheck() {
        ((Activity) mContext).runOnUiThread(new Runnable() {
            @Override
            public void run() {

                String str = String.format(Locale.KOREA, "서비스 시작");
                Toast.makeText(mContext, str, Toast.LENGTH_SHORT).show();

                String[] data  = WalkingAnalysisServiceApi.getUserInfo();
                if (data != null) {
                    // example
                    data[WalkingAnalysisServiceApi.KEY_MY_FOOT_SIZE_INDEX] = "260";
                    data[WalkingAnalysisServiceApi.KEY_MY_AGE_INDEX] = "30";
                    data[WalkingAnalysisServiceApi.KEY_MY_HEIGHT_INDEX] = "180";
                    data[WalkingAnalysisServiceApi.KEY_MY_WEIGHT_INDEX] = "70";
                    data[WalkingAnalysisServiceApi.KEY_MY_SEX_INDEX] = "M";
                    data[WalkingAnalysisServiceApi.KEY_MY_NAME_INDEX] = "user";
                    data[WalkingAnalysisServiceApi.KEY_MY_EMAIL_INDEX] = "user@mgmail.co.kr";

                    WalkingAnalysisServiceApi.setUserInfo(data);
                }

                data = WalkingAnalysisServiceApi.getDeviceAddress();
                /*
                    맨 처음 초기 값 , 기존에 저장해 둔 인솔 주소 , 새롭게 연결할 인솔 주소
                    상황에 맞게 인솔 주소값을 등록하면 된다.
                    First initial value, previously saved Insole address, newly connected Insole address
                    You can register the Insol address value according to the situation.
                    */

                if (data != null) {
                    mLeftAddress = data[WalkingAnalysisServiceApi.KEY_LEFT_ADDRESS_INDEX];
                    mRightAddress = data[WalkingAnalysisServiceApi.KEY_RIGHT_ADDRESS_INDEX];


                    Log.e(TAG,"left insole addr " + mLeftAddress + " right insole addr " + mRightAddress);

                    if (mLeftAddress.equals("00:00:00:00:00:00") && mRightAddress.equals("00:00:00:00:00:01")) {

                    /*
                     맨 처음 초기 값이며, 연결할 인솔을 검색한 후, 저장한 후에 연결 한다.
                     This is the initial value. Search for the insole to be connected, save it, and then connect.
                     */
                    /*
                     아래 코드는 접속할 인솔의 주소이다.
                     그래서 인솔 접속하기 전에 아래와 같은 코드 적용이 된 상태라면.
                     연결을 시도할때 이 주소를 가지고 연결을 한다.

                     The code below is the address of the console to be accessed.
                     So, if the code below has been applied before connecting to Insole.
                     When attempting a connection, the connection is made using this address.

                    */
//                        mLeftAddress = "EB:AB:62:72:B5:7A";
//                        mRightAddress = "FE:BE:07:E6:D4:2B";
//                        mLeftAddress = "E0:4F:48:67:12:6C";
//                        mRightAddress = "CA:5F:72:5A:59:8F";

//                        mLeftAddress = "FF:FE:DC:D8:D3:40";
//                        mRightAddress = "CE:11:9F:93:72:CE";

                        data[WalkingAnalysisServiceApi.KEY_LEFT_ADDRESS_INDEX] = mLeftAddress;
                        data[WalkingAnalysisServiceApi.KEY_RIGHT_ADDRESS_INDEX] = mRightAddress;
                        WalkingAnalysisServiceApi.setDeviceAddress(data);
                    }

//                    mLeftAddress = "E0:4F:48:67:12:6C";
//                    mRightAddress = "CA:5F:72:5A:59:8F";

//                    mLeftAddress = "FF:FE:DC:D8:D3:40";
//                    mRightAddress = "CE:11:9F:93:72:CE";
//
//                    data[WalkingAnalysisServiceApi.KEY_LEFT_ADDRESS_INDEX] = mLeftAddress;
//                    data[WalkingAnalysisServiceApi.KEY_RIGHT_ADDRESS_INDEX] = mRightAddress;

                    WalkingAnalysisServiceApi.setDeviceAddress(data);
                }

            }

        });
    }

    @Override
    public void OnServiceCreated(int result) {
        Log.e(TAG,"Service create result: " + result);
        ServiceStartCheck();
    }

    @Override
    public void OnConnected(int lr_type,int status) {
        Log.e(TAG,"onConnect lr_type " + lr_type +  " status " + status);
    }

    @Override
    public void OnDisconnected(int lr_type,int status) {
        Log.e(TAG,"OnDisconnected lr_type " + lr_type +  " status " + status);
    }

    @Override
    public void OnLeftRealData(byte[] bytes) {
        DataQueuePacket qPacket = new DataQueuePacket();
        qPacket.setData(CommonAttribute.LEFT_TYPE,bytes);
        dataQueue.offer(qPacket);
        synchronized (mWaitLock){
            mWaitLock.notify();
        }
    }

    @Override
    public void OnRightRealData(byte[] bytes) {
        DataQueuePacket qPacket = new DataQueuePacket();
        qPacket.setData(CommonAttribute.RIGHT_TYPE,bytes);
        dataQueue.offer(qPacket);
        synchronized (mWaitLock){
            mWaitLock.notify();
        }
    }

    @Override
    public void OnStorageATData(ATData atData) {
        Log.e(TAG,"OnStorageATData :" + atData);

        runOnUiThread(new Runnable() {
            @Override
            public void run() {

                if(atData.mDate > 0){
                    java.util.Date dt = new java.util.Date(atData.mDate);
                    SimpleDateFormat sdfNow = new SimpleDateFormat("yyyyMMdd-HHmmss");

                    if(atData.mDataType == CommonAttribute.FLASH_DAY_DATA){
                        Log.e(TAG, "OnStorageATData DAY : " +
                                ", mDate = " + sdfNow.format(dt) + ", mDataType = " + atData.mDataType +
                                ", mCalories = "  + atData.mCalories + ", mDistance = " + (atData.mWalkingDistance+atData.mRunningDistance) +
                                ", mLR_Type = " + atData.mLR_Type + ", mStepCount = " + (atData.mWalkingStepCount+atData.mRunningStepCount));

                    }else if(atData.mDataType == CommonAttribute.FLASH_HOUR_DATA){
                        Log.e(TAG, "OnStorageATData HOUR : " +
                                ", mDate = " + sdfNow.format(dt) + ", mDataType = " + atData.mDataType +
                                ", mCalories = "  + atData.mCalories + ", mDistance = " + (atData.mWalkingDistance+atData.mRunningDistance) +
                                ", mLR_Type = " + atData.mLR_Type + ", mStepCount = " + (atData.mWalkingStepCount+atData.mRunningStepCount));
                    }
                }else{
                    // 인솔에서 제공해줄 데이터가 없다.
                    // There is no data provided by Insole.
                    if(atData.mDataType == CommonAttribute.FLASH_DAY_DATA){
                        Log.e(TAG,"day data no");
                    }else if(atData.mDataType == CommonAttribute.FLASH_HOUR_DATA){
                        Log.e(TAG,"hour data no");
                    }
                }
            }
        });

    }

    @Override
    public void OnStorageAAData(AAData aaData) {
        Log.e(TAG,"OnStorageAAData :" + aaData);

        runOnUiThread(new Runnable() {
            @Override
            public void run() {

                if(aaData.mDate > 0){
                    java.util.Date dt = new java.util.Date(aaData.mDate);
                    SimpleDateFormat sdfNow = new SimpleDateFormat("yyyyMMdd-HHmmss");

                    if(aaData.mDataType == CommonAttribute.FLASH_DAY_DATA){
                        Log.e(TAG, "OnStorageAAData DAY : " + aaData.toString());

                    }else if(aaData.mDataType == CommonAttribute.FLASH_HOUR_DATA){
                        Log.e(TAG, "OnStorageAAData HOUR : " + aaData.toString());
                    }
                }else{
                    // 인솔에서 제공해줄 데이터가 없다.
                    // There is no data provided by Insole.
                    if(aaData.mDataType == CommonAttribute.FLASH_DAY_DATA){
                        Log.e(TAG,"day data no");
                    }else if(aaData.mDataType == CommonAttribute.FLASH_HOUR_DATA){
                        Log.e(TAG,"hour data no");
                    }
                }
            }
        });



    }

    @Override
    public void OnStorageGAData(GAData gaData) {
        Log.e(TAG,"OnStorageGAData :mDate:" + gaData.mDate + " mPreDate:" + gaData.mPreDate);

        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                /*
                 mDate 순서
                 ## 데이터가 있을때
                     mDate == -2(유효 데이터 , 다음 데이터 요청한다)
                     -> mDate == 0(인솔 더이상 데이터가 없다, end)
                     -> mDate > 0(라이브러리에서 처리한 데이터 보내줌)
                     -> mDate == -3(라이브러리에서 더이상 데이터 없음 , end)의 순서대로 온다.

                 ## 데이터가 없다면 mDate == 0 -> mDate == -3 의 순서대로 온다.

                 mDate order
                 ## When there is data
                     mDate == -2 (valid data, request next data)
                     -> mDate == 0 (there is no more data, end)
                     -> mDate > 0 (data processed by the library is sent)
                     -> mDate == -3 (no more data in the library, end).

                 ## If there is no data, it comes in the following order: mDate == 0 -> mDate == -3.

                */

                /*
                    왼발 데이터 요청한다.
                    -> 데이터 있으면 (mDate == -2)
                        -> 다시 왼발 요청한다.(데이터가 0일 때 까지)
                    -> 데이터 없으면 (mDate == 0)
                        -> 오른발 데이터 요청

                    오른발 데이터 요청한다.
                     -> 데이터 있으면 (mDate == -2)
                       -> 다시 오른발 데이터 요청한다.(데이터가 0일 때 까지)
                     -> 데이터 없으면 (mDate == 0)
                       -> 라이브러리에서 (mDate > 0) 큰 값으로  전달한다.

                     mDate >0 큰 수
                     왼발 값 들어오고 계속 들어온다(유효 데이터).
                     다 들어오면 (mDate == -3) 값을 준다(유효 데이터 종료).
                     그리고 오른발 (mDate >0) 값으로 계속 들어온다(유효 데이터).
                     다 들어오면 (mDate == -3) 값을 준다(유효 데이터 종료).

                     오른발 (mDate == -3)의 값을 받으면 완료된 상태이다.

                     Request left foot data.
                        -> If there is data (mDate == -2)
                           -> Request the left foot again (until the data is 0)
                        -> If there is no data (mDate == 0)
                           -> Right foot data request

                    Request right foot data.
                        -> If there is data (mDate == -2)
                           -> Request right foot data again (until data is 0)
                        -> If there is no data (mDate == 0)
                           -> In the library, (mDate > 0) is passed as a large value.

                     mDate >0 large number
                     The left foot value comes in and continues to come in (valid data).
                     When everything comes in, a value (mDate == -3) is given (valid data ends).
                     And the right foot (mDate >0) value continues to come in (valid data).
                     When everything comes in, a value (mDate == -3) is given (valid data ends).

                     If you receive the value of the right foot (mDate == -3), it is complete.
                */

                if(gaData.mDate > 0){
                    java.util.Date dt = new java.util.Date(gaData.mDate);
                    SimpleDateFormat sdfNow = new SimpleDateFormat("yyyyMMdd-HHmmss");

                    if(gaData.mDataType == CommonAttribute.FLASH_DAY_DATA){
                        Log.e(TAG, "OnStorageGAData DAY : " + gaData.toString());

                    }else if(gaData.mDataType == CommonAttribute.FLASH_HOUR_DATA){
                        Log.e(TAG, "OnStorageGAData HOUR : " + gaData.toString());
                    }
                }
                else if(gaData.mDate == -2){
                    /*
                     다음 데이터 요청 또는 mPreDate를 이용하여 해당 날짜의 데이터로 이용할 수 있다.
                     You can use the data for that date by requesting the following data or using mPreDate.
                    */
                    java.util.Date dt = new java.util.Date(gaData.mPreDate);
                    SimpleDateFormat sdfNow = new SimpleDateFormat("yyyyMMdd-HHmmss");

                    if(gaData.mDataType == CommonAttribute.FLASH_DAY_DATA){
                        Log.e(TAG, "OnStorageGAData DAY : " + gaData.toString());

                    }else if(gaData.mDataType == CommonAttribute.FLASH_HOUR_DATA){
                        Log.e(TAG, "OnStorageGAData HOUR : " + gaData.toString());
                    }
                }
                else if(gaData.mDate == -3){
                    /*
                     데이터가 없다. 라이브러리에서 모두 전달했다.
                     There is no data. All delivered by the library.
                    */

                    if(gaData.mDataType == CommonAttribute.FLASH_DAY_DATA){
                        Log.e(TAG,"day data no");
                    }else if(gaData.mDataType == CommonAttribute.FLASH_HOUR_DATA){
                        Log.e(TAG,"hour data no");
                    }

                }
                else if(gaData.mDate == 0){
                    /*
                     인솔에서 제공해줄 데이터가 없다.다음 데이터 요청 한다.
                     다음 데이터 요청(mData == -3)이 나오도록 하기 위해서이다.
                     There is no data to provide from Insole. Request the following data.
                     This is to ensure that the next data request (mData == -3) is issued.
                    */
                    if(gaData.mDataType == CommonAttribute.FLASH_DAY_DATA){
                        Log.e(TAG,"day data no");
                    }else if(gaData.mDataType == CommonAttribute.FLASH_HOUR_DATA){
                        Log.e(TAG,"hour data no");
                    }
                }
            }
        });

    }

    @Override
    public void OnStorageMVData(MVData mvData) {
        Log.e(TAG,"OnStorageMVData :mDate:" + mvData.mDate + " mPreDate:" + mvData.mPreDate);
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                /*
                 mDate 순서
                 ## 데이터가 있을때
                     mDate == -2(유효 데이터 , 다음 데이터 요청한다)
                     -> mDate == 0(인솔 더이상 데이터가 없다, end)
                     -> mDate > 0(라이브러리에서 처리한 데이터 보내줌)
                     -> mDate == -3(라이브러리에서 더이상 데이터 없음 , end)의 순서대로 온다.

                 ## 데이터가 없다면 mDate == 0 -> mDate == -3 의 순서대로 온다.

                 mDate order
                 ## When there is data
                     mDate == -2 (valid data, request next data)
                     -> mDate == 0 (there is no more data, end)
                     -> mDate > 0 (data processed by the library is sent)
                     -> mDate == -3 (no more data in the library, end).

                 ## If there is no data, it comes in the following order: mDate == 0 -> mDate == -3.

                */

                /*
                    왼발 데이터 요청한다.
                    -> 데이터 있으면 (mDate == -2)
                        -> 다시 왼발 요청한다.(데이터가 0일 때 까지)
                    -> 데이터 없으면 (mDate == 0)
                        -> 오른발 데이터 요청

                    오른발 데이터 요청한다.
                     -> 데이터 있으면 (mDate == -2)
                       -> 다시 오른발 데이터 요청한다.(데이터가 0일 때 까지)
                     -> 데이터 없으면 (mDate == 0)
                       -> 라이브러리에서 (mDate > 0) 큰 값으로  전달한다.

                     mDate >0 큰 수
                     왼발 값 들어오고 계속 들어온다(유효 데이터).
                     다 들어오면 (mDate == -3) 값을 준다(유효 데이터 종료).
                     그리고 오른발 (mDate >0) 값으로 계속 들어온다(유효 데이터).
                     다 들어오면 (mDate == -3) 값을 준다(유효 데이터 종료).

                     오른발 (mDate == -3)의 값을 받으면 완료된 상태이다.

                     Request left foot data.
                        -> If there is data (mDate == -2)
                           -> Request the left foot again (until the data is 0)
                        -> If there is no data (mDate == 0)
                           -> Right foot data request

                    Request right foot data.
                        -> If there is data (mDate == -2)
                           -> Request right foot data again (until data is 0)
                        -> If there is no data (mDate == 0)
                           -> In the library, (mDate > 0) is passed as a large value.

                     mDate >0 large number
                     The left foot value comes in and continues to come in (valid data).
                     When everything comes in, a value (mDate == -3) is given (valid data ends).
                     And the right foot (mDate >0) value continues to come in (valid data).
                     When everything comes in, a value (mDate == -3) is given (valid data ends).

                     If you receive the value of the right foot (mDate == -3), it is complete.
                */

                if(mvData.mDate > 0){
                    java.util.Date dt = new java.util.Date(mvData.mDate);
                    SimpleDateFormat sdfNow = new SimpleDateFormat("yyyyMMdd-HHmmss");

                    if(mvData.mDataType == CommonAttribute.FLASH_DAY_DATA){
                        Log.e(TAG, "OnStorageMVData DAY : " + mvData.toString());

                    }else if(mvData.mDataType == CommonAttribute.FLASH_HOUR_DATA){
                        Log.e(TAG, "OnStorageMVData HOUR : " + mvData.toString());
                    }
                }
                else if(mvData.mDate == -2){
                    /*
                     다음 데이터 요청 또는 mPreDate를 이용하여 해당 날짜의 데이터로 이용할 수 있다.
                     You can use the data for that date by requesting the following data or using mPreDate.
                    */
                    java.util.Date dt = new java.util.Date(mvData.mPreDate);
                    SimpleDateFormat sdfNow = new SimpleDateFormat("yyyyMMdd-HHmmss");

                    if(mvData.mDataType == CommonAttribute.FLASH_DAY_DATA){
                        Log.e(TAG, "OnStorageMVData DAY : " + mvData.toString());

                    }else if(mvData.mDataType == CommonAttribute.FLASH_HOUR_DATA){
                        Log.e(TAG, "OnStorageMVData HOUR : " + mvData.toString());
                    }
                }
                else if(mvData.mDate == -3){
                    /*
                     데이터가 없다. 라이브러리에서 모두 전달했다.
                     There is no data. All delivered by the library.
                    */

                    if(mvData.mDataType == CommonAttribute.FLASH_DAY_DATA){
                        Log.e(TAG,"day data no");
                    }else if(mvData.mDataType == CommonAttribute.FLASH_HOUR_DATA){
                        Log.e(TAG,"hour data no");
                    }

                }
                else if(mvData.mDate == 0){
                    /*
                     인솔에서 제공해줄 데이터가 없다.다음 데이터 요청 한다.
                     다음 데이터 요청(mData == -3)이 나오도록 하기 위해서이다.
                     There is no data to provide from Insole. Request the following data.
                     This is to ensure that the next data request (mData == -3) is issued.
                    */
                    if(mvData.mDataType == CommonAttribute.FLASH_DAY_DATA){
                        Log.e(TAG,"day data no");
                    }else if(mvData.mDataType == CommonAttribute.FLASH_HOUR_DATA){
                        Log.e(TAG,"hour data no");
                    }
                }
            }
        });


    }

    @Override
    public void OnATData(ATData atData) {

        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Log.e(TAG,"OnATData:"+atData.toString());

                // OnSAData callback
                WalkingAnalysisServiceApi.getStatistics();
            }
        });

    }

    @Override
    public void OnAAData(AAData aaData) {

        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Log.e(TAG,"OnAAData:"+aaData.toString());
            }
        });
    }

    @Override
    public void OnGAData(GAData gaData) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Log.e(TAG,"OnGAData:"+gaData.toString());
            }
        });
    }

    @Override
    public void OnMVData(MVData mvData) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Log.e(TAG,"OnMVData:"+mvData.toString());
            }
        });
    }

    @Override
    public void OnSAData(SAData saData) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Log.e(TAG,"OnSAData:"+saData.toString());
            }
        });
    }


    /*
            연결할 인솔을 검색한다.
            연결할 이름이 검색 되면 해당되는 인솔의 주소를 알아둔다.
            LEFT INSOLE NAME(scanData.getDeviceName()) -> PSPODL
            RIGHT INSOLE NAME(scanData.getDeviceName()) -> PSPODR
            Search for the insole to connect.
            When a name to connect is searched, find out the address of the corresponding guide.
            LEFT INSOLE NAME(scanData.getDeviceName()) -> PSPODL
            RIGHT INSOLE NAME(scanData.getDeviceName()) -> PSPODR

   */
    @Override
    public void OnScanData(ScanData scanData) {
        Log.e(TAG,"OnScanData " + scanData.toString());
//        Log.d(TAG, "OnScanDataAddress: " + scanData.getDeviceAddress());
        // 从 scanData 中获取设备地址
        String deviceAddress = scanData.getDeviceAddress();

        if (mDeviceAddresses[0] == null && scanData.getDeviceName().equals("PSPODL")) {
            mDeviceAddresses[0] = deviceAddress;
            mLeftAddress = deviceAddress;
        } else if (mDeviceAddresses[1] == null && scanData.getDeviceName().equals("PSPODR") /*mDeviceAddresses[0] != deviceAddress*/) {
            mDeviceAddresses[1] = deviceAddress;
            mRightAddress = deviceAddress;
        }

        // 如果是一个新的地址,将它添加到数组中
//        if (mDeviceAddresses[0] == null ) {
//            mDeviceAddresses[0] = deviceAddress;
//        } else if (mDeviceAddresses[1] == null && mDeviceAddresses[0] != deviceAddress) {
//            mDeviceAddresses[1] = deviceAddress;
//        }
        // Modify the value of PreferenceData
        if(mDeviceAddresses[0] != null && mDeviceAddresses[1] != null ) {
            String[] deviceInfoPreference = PreferenceData.getDeviceInfoPreference(this);

            deviceInfoPreference[PreferenceData.left_device_address_index] = mDeviceAddresses[0];
            deviceInfoPreference[PreferenceData.right_device_address_index] = mDeviceAddresses[1];
            PreferenceData.setDeviceInfoPreference(this,deviceInfoPreference);

            // Connect to insoles
            // connectToInsoles();

            if (mDeviceAddresses[0] != null && mDeviceAddresses[1] != null) {
                // 인솔이 검색이 되었다면 scan stop 한다.
                // If the insole is searched, scan stop.
                WalkingAnalysisServiceApi.stopDeviceScan();

            }

        }


    }

//    private void connectToInsoles() {
//        String[] data = WalkingAnalysisServiceApi.getDeviceAddress();
//
//        if (data != null) {
//            mLeftAddress = data[WalkingAnalysisServiceApi.KEY_LEFT_ADDRESS_INDEX];
//            mRightAddress = data[WalkingAnalysisServiceApi.KEY_RIGHT_ADDRESS_INDEX];
//
//            Log.e(TAG,"left insole addr " + mLeftAddress + " right insole addr " + mRightAddress);
//
//            data[WalkingAnalysisServiceApi.KEY_LEFT_ADDRESS_INDEX] = mLeftAddress;
//            data[WalkingAnalysisServiceApi.KEY_RIGHT_ADDRESS_INDEX] = mRightAddress;
//            WalkingAnalysisServiceApi.setDeviceAddress(data);
//        }
//    }


    @Override
    public void OnInitializeCompleted() {
        Log.e(TAG,"OnInitializeCompleted Left , Right All Connected");
        ((Activity) mContext).runOnUiThread(new Runnable() {
            public void run() {
                // 매번 접속시 사용자 변경 데이터 넣어준다.
                // User change data is entered every time you connect.
                WalkingAnalysisServiceApi.setDeviceInformation((byte)CommonAttribute.REGISTER_TYPE_CHANGE_USERINFO);
            }
        });
    }

    @Override
    public void OnDeviceInfomation(int lr_type,DeviceInfo info,int command_type) {
        Log.e(TAG,"OnDeviceInfomation lr_type" + lr_type + " info " +info.toString());
    }


    @Override
    public void OnRealtime(int lr_type,byte enable) {
        Log.e(TAG,"OnRealtime lr_type" + lr_type + " enable " +enable);
    }

    @Override
    public void OnRequestStatus(int lr_type,byte status) {
        Log.e(TAG,"OnRequestStatus lr_type" + lr_type + " status " +status);
    }

    @Override
    public void OnClearHistory(int lr_type,byte cleartype,byte done) {
        Log.e(TAG,"OnClearHistory lr_type" + lr_type + " cleartype " +cleartype + " done:" +done);
    }

    @Override
    public void OnClearAll(int lr_type,byte done) {
        Log.e(TAG,"OnClearAll lr_type" + lr_type + " done:" +done);
    }

    @Override
    public void OnGetVersion(int lr_type,byte hwv,byte fwv,int bv,int finaltest) {
        Log.e(TAG,"OnGetVersion lr_type" + lr_type + " hwv:" +hwv + " fwv:"+fwv + " bv:"+bv + " finaltest:"+finaltest);
    }

    @Override
    public void OnGetFWStatus(int lr_type, int batterylevel,
                              int currentrtc, int temperature, int memsstatus,
                              int memsenable, int fsrstatus, int fsrenable,int bootcount) {

        // GMT Data Korea
        TimeZone tz = TimeZone.getDefault();
        // currentrtc 안에는 GMT + TIME ZONE이 들어가 있다. 그래서 TIME ZONE 만큼 뺐다.
        // Currentrtc contains GMT + TIME ZONE. So it's all about TIME ZONE.
        Date dt = new Date(currentrtc*1000L);
        SimpleDateFormat sdfNow = new SimpleDateFormat("yyyy_MM_dd HH_mm_ss", Locale.getDefault());
        if(ApiBuildConfig.DEBUG)Log.e(TAG,"currentrtc*1000L Date " + sdfNow.format(dt));

        // currentrtc 안에는 GMT + TIME ZONE이 들어가 있다. 그래서 TIME ZONE 만큼 뺐다.
        // Currentrtc contains GMT + TIME ZONE. So it's all about TIME ZONE.
        dt = new Date(currentrtc*1000L - tz.getRawOffset());
        sdfNow = new SimpleDateFormat("yyyy_MM_dd HH_mm_ss", Locale.getDefault());
        Log.e(TAG,"currentrtc*1000L - tz.getRawOffset() Date " + sdfNow.format(dt) + " offset " + tz.getRawOffset());



        Log.e(TAG,"OnGetFWStatus " + lr_type +  " batterylevel " + batterylevel + " currentrtc " + currentrtc + " temperature " + temperature
                + " memsenable " + memsenable + " memsstatus " + memsstatus + " fsrstatus " + fsrstatus + " fsrenable " + fsrenable);

    }


    @Override
    public void OnUpdateProgress(String lr_type_address,int value) {
        Log.e(TAG,"OnUpdateProgress lr_type_address" + lr_type_address + " value:" +value);
    }

    @Override
    public void OnUpdateResult(String lr_type_address,int result) {
        Log.e(TAG,"OnUpdateResult lr_type_address" + lr_type_address + " result:" +result);
    }

    @Override
    public void OnSetFindDevice(int lr_type,int enablemode) {
        Log.e(TAG,"OnSetFindDevice " + lr_type + " enablemode " + enablemode );
    }

    @Override
    public void OnGetFindDevice(int lr_type, int mode, int enable) {
        Log.e(TAG,"OnGetFindDevice " + lr_type + " mode " + mode + " detected " + enable );

    }

    @Override
    public void OnRunningStartStop(RunningData runningData) {
        Log.e(TAG,"OnRunningStartStop  runningData:"+runningData);
    }

    @Override
    public void OnRunningGetSection(RunningSectionData runningSectionData) {
        Log.e(TAG,"OnRunningGetSection  runningSectionData:"+runningSectionData);
    }


    Runnable mFootFileViewRunnable = new Runnable(){
        @Override
        public void run() {

            int [] fdata = new int[4];
            Log.e(TAG, TAG+" Thread start");
            while(mFootFileViewEnable){

                synchronized (mWaitLock) {
                    if(dataQueue == null)
                        return;

                    if(dataQueue.size()  > 0){
                        DataQueuePacket packet = null;
                        try {
                            packet = dataQueue.take();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }

                        byte arg0 [] = packet.org;


                        if (packet.lr_type == CommonAttribute.LEFT_TYPE) {
                            ArrayList<MpuData> mpulist = WalkingAnalysisServiceApi.getImuDataList(CommonAttribute.LEFT_TYPE, arg0);
                            ArrayList<FsrData> fsrlist = WalkingAnalysisServiceApi.getFsrDataList(CommonAttribute.LEFT_TYPE);
                            for (int i = 0; i < fsrlist.size(); i++) {
                                FsrData fsrdata = fsrlist.get(i);
                                MpuData mpudata = mpulist.get(i);
                                // System.out.println(" Left X:" + mpudata.mMpu[0] + " Y:" + mpudata.mMpu[1] + " Z:" + mpudata.mMpu[2]);
                                // System.out.println(" Left f1:" + fsrdata.mFsr[0] + " f2:" + fsrdata.mFsr[1] + " f3:" + fsrdata.mFsr[2]+ " f4:" + fsrdata.mFsr[3]);
                                CSVWriter.writeStringToCsv(internalStorageDir," Left X:" + mpudata.mMpu[0] + " Y:" + mpudata.mMpu[1] + " Z:" + mpudata.mMpu[2]+" Left f1:" + fsrdata.mFsr[0] + " f2:" + fsrdata.mFsr[1] + " f3:" + fsrdata.mFsr[2]+ " f4:" + fsrdata.mFsr[3],"left");
                                Log.e(TAG, TAG+" Left X:" + mpudata.mMpu[0] + " Y:" + mpudata.mMpu[1] + " Z:" + mpudata.mMpu[2]);
                                Log.e(TAG, TAG+" Left f1:" + fsrdata.mFsr[0] + " f2:" + fsrdata.mFsr[1] + " f3:" + fsrdata.mFsr[2]+ " f4:" + fsrdata.mFsr[3]);
                            }
                        }else{
                            ArrayList<MpuData> mpulist = WalkingAnalysisServiceApi.getImuDataList(CommonAttribute.RIGHT_TYPE, arg0);
                            ArrayList<FsrData> fsrlist = WalkingAnalysisServiceApi.getFsrDataList(CommonAttribute.RIGHT_TYPE);
                            for (int i = 0; i < fsrlist.size(); i++) {
                                FsrData fsrdata = fsrlist.get(i);
                                MpuData mpudata = mpulist.get(i);
                                CSVWriter.writeStringToCsv(internalStorageDir," Right X:" + mpudata.mMpu[0] + " Y:" + mpudata.mMpu[1] + " Z:" + mpudata.mMpu[2]+" Right f1:" + fsrdata.mFsr[0] + " f2:" + fsrdata.mFsr[1] + " f3:" + fsrdata.mFsr[2]+ " f4:" + fsrdata.mFsr[3],"right");
                                Log.e(TAG, TAG+" Right X:" + mpudata.mMpu[0] + " Y:" + mpudata.mMpu[1] + " Z:" + mpudata.mMpu[2]);
                                Log.e(TAG, TAG+" Right f1:" + fsrdata.mFsr[0] + " f2:" + fsrdata.mFsr[1] + " f3:" + fsrdata.mFsr[2]+ " f4:" + fsrdata.mFsr[3]);
                            }
                        }
                    }else{
                        try {
                            mWaitLock.wait(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
            Log.e(TAG, TAG+"Thread end");
        }
    };



}